package utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
public class DateUtil {
    /**
     * 日期格式 年月日
     */
    private static final String PATTERN1="yyyy-MM-dd";

    /**
     * 日期格式 年月日 时分秒
     */
    private static final String PATTERN2="yyyy-MM-dd HH:mm:ss";

    /**
     * 日期格式 年月日
     */
    private static final String PATTERN3="yyyy年MM月dd日";

    /**
     * 日期格式 年月日 时分秒
     */
    private static final String PATTERN4="yyyy年MM月dd日 HH时mm分ss秒";

    /**
     * 日期格式 年月日
     */
    private static final String PATTERN5="yyyy/MM/dd";

    /**
     * 日期格式 年月日 时分秒
     */
    private static final String PATTERN6="yyyy/MM/dd日 HH/mm/ss";

    /**
     * 应用模式 :采用 年月日
     */
    public static  String APPLY_PATTERN=PATTERN1;

    /**
     * 默认年月日 时分秒
     */
    private static final SimpleDateFormat sdf = new SimpleDateFormat(APPLY_PATTERN);

    /**
     *
     * @param pattern 日期格式  例如:yyyy-MM-dd
     * @return {@link String}
     */
    public static String getDate(Date date, String pattern){
        if (null!=pattern && !pattern.isEmpty()){
            return new SimpleDateFormat(pattern).format(date);
        }
        return sdf.format(new Date());
    }

    /**
     *  获取当前日期
     * @return {@link String}
     */
    public static String getNowDate(){
        return sdf.format(new Date());
    }

    /**
     * 获取间隔月份  例如2023-01 到2023-12  startDate:2023-01   endDate:2023-12
     *
     * @param startDate       开始日期
     * @param endDate         结束日期
     * @param splitSign       分割符号,默认是 -
     * @param resultSplitSign 返回集合的日期分割符 默认是-
     * @return {@link List}<{@link String}>
     */
    public static List<String> getIntervalMonths(String startDate, String endDate, String splitSign, String resultSplitSign){
        List<String> monthListRes=new ArrayList<>();
        if (splitSign.isEmpty()){splitSign="-";}
        if (resultSplitSign.isEmpty()){resultSplitSign="-";}
        int startYear = Integer.parseInt(startDate.trim().split(splitSign)[0]);
        int startMonth = Integer.parseInt(startDate.trim().split(splitSign)[1]);
        int endYear = Integer.parseInt(endDate.trim().split(splitSign)[0]);
        if (startYear>endYear){
            System.err.println("开始年份不能比结束年份大!");
            return null;
        }
        int endMonth = Integer.parseInt(endDate.trim().split(splitSign)[1]);
        //不跨年情况
        if (startYear==endYear){
            if (startMonth == endMonth){
                //同月
                return new ArrayList<String>(){{add(startDate);}};
            }else {
                //不同月
                for (int i = startMonth; i <=endMonth ; i++) {
                    if (i<10){
                        monthListRes.add(startYear+resultSplitSign+"0"+i);
                        continue;
                    }
                    monthListRes.add(startYear+resultSplitSign+i);
                }
                return monthListRes;
            }

        }
        //年份遍历
        for (int i = startYear; i <endYear ; i++) {
            //月份遍历
            //把今年的月份结算
            for (int j = startMonth; j <=12 ; j++) {
                if (j<10){
                    monthListRes.add(startYear+resultSplitSign+"0"+j);
                    continue;
                }
                monthListRes.add(startYear+resultSplitSign+j);
            }
            startYear++;
            if (startYear==endYear){
                //剩下月份
                for (int j = 1; j <= endMonth; j++) {
                    if (j<10){
                        monthListRes.add(startYear+resultSplitSign+"0"+j);
                        continue;
                    }
                    monthListRes.add(startYear+resultSplitSign+j);
                }
            }
        }
        //跨年情况
        return monthListRes;
    }

    /**
     * 获取间隔月份  例如2023-01 到2023-12  startDate:2023-01   endDate:2023-12
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @return {@link List}<{@link String}>
     */
    public static List<String> getIntervalMonths(String startDate, String endDate){
        return getIntervalMonths(startDate,endDate,"","");
    }


    /**
     * 获取本周的所有日期
     * @return {@link List}<{@link Date}>
     */
    public static List<Date> getDatesInWeek() {
        List<Date> dates = new ArrayList<>();
        Date currentDate = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(currentDate);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        int daysFromMonday = dayOfWeek - Calendar.MONDAY;
        int daysToSunday = Calendar.SUNDAY - dayOfWeek;
        calendar.add(Calendar.DATE, daysFromMonday);
        Date startDate = calendar.getTime();
        calendar.add(Calendar.DATE, daysToSunday);
        Date endDate = calendar.getTime();
        calendar.setTime(startDate);
        while (!calendar.getTime().after(endDate)) {
            dates.add(calendar.getTime());
            calendar.add(Calendar.DATE, 1);
        }
        return dates;
    }


    /**
     * 获取指定日期所在周 的所有日期
     * @param time 日期格式 yyyy-MM-dd
     * @return {@link List}<{@link String}>
     */
    public static List<String> getWeekByDate(String time) {
        List<String> dayList=new ArrayList<>();
        Date date;
        try {
            date = sdf.parse(time);
        } catch (ParseException e) {
            e.printStackTrace();
            System.out.println("传入的日期格式不合法!");
            throw new RuntimeException(e);
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        // 判断要计算的日期是否是周日，如果是则减一天计算周六的，否则会出问题，计算到下一周去了
        int dayWeek = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
        if (1 == dayWeek) {
            cal.add(Calendar.DAY_OF_MONTH, -1);
        }
        cal.setFirstDayOfWeek(Calendar.MONDAY);// 设置一个星期的第一天，按中国的习惯一个星期的第一天是星期一
        int day = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
        cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);// 根据日历的规则，给当前日期减去星期几与一个星期第一天的差值
        for (int i = 0; i <=6; i++) {
            dayList.add( sdf.format(cal.getTime()));
            cal.add(Calendar.DATE, 1);
        }
        return dayList;

    }


    /**
     * 获取指定日期所在周 的所有日期
     * @param time 日期
     * @return {@link List}<{@link String}>
     */
    public static List<String> getWeekByDate(Date time) {
        List<String> dayList=new ArrayList<>();
        Calendar cal = Calendar.getInstance();
        cal.setTime(time);
        // 判断要计算的日期是否是周日，如果是则减一天计算周六的，否则会出问题，计算到下一周去了
        int dayWeek = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
        if (1 == dayWeek) {
            cal.add(Calendar.DAY_OF_MONTH, -1);
        }
        cal.setFirstDayOfWeek(Calendar.MONDAY);// 设置一个星期的第一天，按中国的习惯一个星期的第一天是星期一
        int day = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
        cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);// 根据日历的规则，给当前日期减去星期几与一个星期第一天的差值
        for (int i = 0; i <=6; i++) {
            dayList.add( sdf.format(cal.getTime()));
            cal.add(Calendar.DATE, 1);
        }
        return dayList;

    }

    /**
     * 获取本周内所有日期
     * @return {@link List}<{@link String}>
     */
    public static List<String> getWeekByDate() {
        return getWeekByDate(new Date());
    }

    /**
     * 获取日期
     * @param targetDate 传入的日期
     * @param days  相差的天数  正数往前， 负数往后
     * @return {@link Date}
     */
    public static Date getDate(Date targetDate,Integer days){
        Calendar cal = Calendar.getInstance();
        cal.setTime(targetDate);
        cal.add(Calendar.DATE, days);
        return cal.getTime();
    }


    /**
     * 获取日期
     * @param targetDate 传入的日期 日期格式 yyyy-MM-dd
     * @param days  相差的天数  正数往前， 负数往后
     * @return {@link Date}
     */
    public static String getDate(String targetDate,Integer days){
        Calendar cal = Calendar.getInstance();
        try {
            cal.setTime(sdf.parse(targetDate));
        } catch (ParseException e) {
            e.printStackTrace();
            System.out.println("传入日期字符串格式不是yyyy-MM-dd类型！");
            throw new RuntimeException(e);
        }
        cal.add(Calendar.DATE, days);
        return  sdf.format(cal.getTime()) ;
    }

    /**
     * 获取指定日期 所在周的第一天
     * @param date 日期 yyyy-MM-dd
     * @return {@link String}
     */
    public static String getFirstDayByWeek(String date){
        return getWeekByDate(date).get(0);
    }


    /**
     * 获取该日期所在周的第一天
     * @param date 日期
     * @return {@link Date}
     */
    public static Date getFirstDayByWeek(Date date){
        String dateStr = sdf.format(date);
        try {
            return sdf.parse(getWeekByDate(dateStr).get(0)) ;
        } catch (ParseException e) {
            System.out.println("日期格式不正确! 正确格式:yyyy-MM-dd");
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取该日期 所在周的最后一天
     * @param date 日期 yyyy-MM-dd
     * @return {@link String}
     */
    public static String getLastDayByWeek(String date){
        return getWeekByDate(date).get(getWeekByDate(date).size()-1);
    }

    /**
     * 获取该日期所在周的 最后一天
     * @param date 日期
     * @return {@link Date}
     */
    public static Date getLastDayByWeek(Date date){
        String dateStr = sdf.format(date);
        try {
            return sdf.parse(getWeekByDate(dateStr).get(getWeekByDate(dateStr).size()-1));
        } catch (ParseException e) {
            System.out.println("日期格式不正确! 正确格式:yyyy-MM-dd");
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取指定日期所在月份的第一天
     * @param year 年份
     * @param month 月份
     * @return {@link Date}
     */
    public static Date getFirstDayByMonth(int year, int month) {
        // 获取Calendar类的实例
        Calendar c = Calendar.getInstance();
        // 设置年份
        c.set(Calendar.YEAR, year);
        // 设置月份，因为月份从0开始，所以用month - 1
        c.set(Calendar.MONTH, month - 1);
        // 设置日期
        c.set(Calendar.DAY_OF_MONTH, 1);
        return c.getTime();
    }

    /**
     * 获取指定日期所在月份的第一天
     * @param year  年份
     * @param month 月份  字符串类型
     * @return {@link String}
     */
    public static String getFirstDayByMonth(String year, int month) {
        // 获取Calendar类的实例
        Calendar c = Calendar.getInstance();
        // 设置年份
        int y = Integer.parseInt(year);
        c.set(Calendar.YEAR, y);
        // 设置月份，因为月份从0开始，所以用month - 1
        c.set(Calendar.MONTH, month - 1);
        // 设置日期
        c.set(Calendar.DAY_OF_MONTH, 1);
        return sdf.format(c.getTime()) ;
    }

    /**
     * 获取指定日期所在月份的最后一天
     * @param year 年份
     * @param month 月份
     * @return {@link Date}
     */
    public static String getLastDayByMonth(String year, int month) {
        // 获取Calendar类的实例
        Calendar c = Calendar.getInstance();
        // 设置年份
        int y = Integer.parseInt(year);
        c.set(Calendar.YEAR, y);
        // 设置月份，因为月份从0开始，所以用month - 1
        c.set(Calendar.MONTH, month - 1);
        // 获取当前时间下，该月的最大日期的数字
        int lastDay = c.getActualMaximum(Calendar.DAY_OF_MONTH);
        // 将获取的最大日期数设置为Calendar实例的日期数
        c.set(Calendar.DAY_OF_MONTH, lastDay);
        return sdf.format(c.getTime()) ;
    }

    /**
     * 获取某年 某月 的最后一天
     * @param year 年份
     * @param month 月份
     * @return {@link Date}
     */
    public static Date getLastDayByMonth(int year, int month) {
        // 获取Calendar类的实例
        Calendar c = Calendar.getInstance();
        // 设置年份
        c.set(Calendar.YEAR, year);
        // 设置月份，因为月份从0开始，所以用month - 1
        c.set(Calendar.MONTH, month - 1);
        // 获取当前时间下，该月的最大日期的数字
        int lastDay = c.getActualMaximum(Calendar.DAY_OF_MONTH);
        // 将获取的最大日期数设置为Calendar实例的日期数
        c.set(Calendar.DAY_OF_MONTH, lastDay);
        return c.getTime();
    }

    /**
     * 获取当前年份当月所有周六周日天数
     * @return {@link Integer}
     */
    public static Integer getCurrentWeekendNumsByMonth(){
        Calendar calendar=Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR); //获取当前年份
        int month = calendar.get(Calendar.MONTH);
        Calendar cal = new GregorianCalendar(year, month, 1);
        int i=0;
        do {
            int day = cal.get(Calendar.DAY_OF_WEEK);
            // 验证是否为 星期六或星期天
            if (day == Calendar.SATURDAY || day == Calendar.SUNDAY) {
                // 打印相应的日期
                i+=1;
                System.out.println(cal.get(Calendar.DAY_OF_MONTH));
            }
            // 下一天
            cal.add(Calendar.DAY_OF_YEAR, 1);
        } while (cal.get(Calendar.MONTH) == month);

        return i;
    }

    /**
     * 获取指定年当前月,所有的周六周日 日期字符串 例如 2023-12-16
     * @param year 年份
     * @return {@link List}<{@link String}>
     */
    public static List<String> getCurrentWeekendNumsByMonth(Integer year){
        Calendar calendar=Calendar.getInstance();
        int month = calendar.get(Calendar.MONTH);
        Calendar cal = new GregorianCalendar(year, month, 1);
        List<String> dateStr=new ArrayList<>();
        do {
            int day = cal.get(Calendar.DAY_OF_WEEK);
            // 验证是否为 星期六或星期天
            if (day == Calendar.SATURDAY || day == Calendar.SUNDAY) {
                // 获取具体天数
                int days = cal.get(Calendar.DAY_OF_MONTH);
                if (days<10){
                    dateStr.add(year +"-"+month+ "-" + "0"+days);
                }else {
                    dateStr.add(year +"-"+month+ "-" + days);
                }
            }
            // 下一天
            cal.add(Calendar.DAY_OF_YEAR, 1);
        } while (cal.get(Calendar.MONTH) == month);

        return dateStr;
    }

    /**
     * 获取 某年某月 的周六周日的 日期字符串
     * @param year  年份
     * @param month 月份 国人计时 1-12月 即可
     * @return {@link List}<{@link String}>
     */

    public static List<String> getCurrentWeekendNumsByMonth(Integer year,Integer month){
        month=month-1;
        Calendar cal = new GregorianCalendar(year, month, 1);
        List<String> dateStrList=new ArrayList<>();
        do {
            int day = cal.get(Calendar.DAY_OF_WEEK);
            // 验证是否为 星期六或星期天
            if (day == Calendar.SATURDAY || day == Calendar.SUNDAY) {
                // 获取具体天数
                int days = cal.get(Calendar.DAY_OF_MONTH);
                if (days<10){
                    dateStrList.add(year +"-"+(month+1)+ "-" + "0"+days);
                }else {
                    dateStrList.add(year +"-"+(month+1)+ "-" + days);
                }
            }
            // 下一天
            cal.add(Calendar.DAY_OF_YEAR, 1);
        } while (cal.get(Calendar.MONTH) == month);

        return dateStrList;
    }

    /**
     * 获取当前月份
     *
     * @param isForeign 是否是国外 0-11
     * @return {@link Integer}
     */
    public static Integer getCurrentMonth(boolean isForeign){
        Calendar calendar = Calendar.getInstance();
        return isForeign ? calendar.get(Calendar.MONTH):calendar.get(Calendar.MONTH)+1;
    }


    /**
     * 获取某年 某月份的天 数
     * @param month 月份  1-12
     * @return {@link Integer}
     */
    public static Integer getAllDaysNumByYearAndMonth(Integer year,Integer month){
        YearMonth yearMonth = YearMonth.of(year, month);
        return yearMonth.lengthOfMonth();
    }

    /**
     * 获某年某月 的天数,默认年是当前年,月是当前月
     * @return {@link Integer}
     */
    public static Integer getCurrentMonthAllDaysNum(){
        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR); //获取当前年份
        int month = calendar.get(Calendar.MONTH) + 1;
        return getAllDaysNumByYearAndMonth(year,month);
    }


    /**
     *  获取间隔日期字符串
     * @param StartDateStr xx年-xx月-xx日
     * @param endDateStr xx年-xx月-xx日
     * @return {@link List}<{@link String}>
     */
    public static List<String> getIntervalDateStr(String StartDateStr,String endDateStr){
        List<String> dateStrRes=new ArrayList<>();
        //开始日期
        LocalDate startDate = LocalDate.parse(StartDateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        //结束日期
        LocalDate endDate = LocalDate.parse(endDateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        //间隔    包含当天需要加1
        long numOfDays = ChronoUnit.DAYS.between(startDate, endDate) + 1L;
        List<LocalDate> listOfDates1 = LongStream.range(0, numOfDays)
                .mapToObj(startDate::plusDays)//映射
                .collect(Collectors.toList());//收集
        DateTimeFormatter formatter =DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 定义日期格式化模式
        for (LocalDate localDate : listOfDates1) {
            String dateStr = localDate.format(formatter); // 将LocalDate对象按指定格式转换为字符串
            dateStrRes.add(dateStr);
        }
        return dateStrRes;
    }


    /**
     * 获取日期字符串里面 日期最大和最小的那个   List内容 0是小日期   1是大日期
     *
     * @param dateStrList    日期集合
     * @param dateStrListSdf 日期集合里面的格式 例如: yyyy-MM-dd
     * @param backSdf       需自行设置返回的日期格式 yyyy-MM-dd
     * @return {@link List}<{@link String}>
     */
    public static List<String> getDateStrListMinAndMax(List<String> dateStrList,SimpleDateFormat dateStrListSdf,SimpleDateFormat backSdf){
        List<Date> dates = new ArrayList<>(); // 存放Date对象列表

        for (String dateStr : dateStrList) {
            Date date;
            try {
                date = dateStrListSdf.parse(dateStr);
            } catch (ParseException e) {
                System.out.println("格式转换失败！");
                throw new RuntimeException(e);
            }
            dates.add(date);
        }
        dates.sort(Comparator.reverseOrder()); // 降序排序
        Date maxDate = dates.get(0); // 获取最大日期
        Collections.sort(dates); // 升序排序

        Date minDate = dates.get(0); // 获取最小日期
        return new ArrayList<String>(){{
            add(0,backSdf.format(minDate));
            add(backSdf.format(maxDate));
        }};
    }

    /**
     * 获取日期字符串里面 日期最大和最小的那个 返回日期格式和全局sdf 格式一致    List内容 0是小日期   1是大日期
     *
     * @param dateStrList    日期集合
     * @param dateStrListSdf 日期集合里面的格式 例如: yyyy-MM-dd
     * @return {@link List}<{@link String}>
     */
    public static List<String> getDateStrListMinAndMax(List<String> dateStrList,SimpleDateFormat dateStrListSdf){
        return getDateStrListMinAndMax(dateStrList,dateStrListSdf,sdf);
    }

    /**
     * 获取日期字符串里面 日期最大和最小的那个 默认数据集合里面的日期格式为 yyyy-MM-dd 返回日期格式和全局sdf 格式一致   List内容 0是小日期   1是大日期
     *
     * @param dateStrList    日期集合
     * @return {@link List}<{@link String}>
     */
    public static List<String> getDateStrListMinAndMax(List<String> dateStrList){
        return getDateStrListMinAndMax(dateStrList,sdf,sdf);
    }

    /**
     *  获取目标日期 和 待比较日期中 相同的日期
     * @param targetDateList  目标日期集合
     * @param compareDateList 要比较的日期字符串 集合
     * @return boolean
     */
    public static List<String> getContainsDate(List<String> targetDateList,List<String> compareDateList){
        return targetDateList.stream().filter(compareDateList::contains).collect(Collectors.toList());
    }


    /**
     *  判断目标日期 是不是在待比较日期中
     * @param targetDateList  目标日期集合
     * @param compareDateList 要比较的日期字符串 集合
     * @return boolean
     */
    public static boolean isContainsDate(List<String> targetDateList,List<String> compareDateList){
        return !getContainsDate(targetDateList, compareDateList).isEmpty();
    }

    /**
     * 日期字符集合转换为Date日期集合   日期字符串的格式必须和 SimpleDateFormat 的格式一致
     * @param strDateList 日期字符串
     * @param sdf SimpleDateFormat
     * @return {@link List}<{@link Date}>
     */
    public static List<Date> strListToDateList(List<String> strDateList,SimpleDateFormat sdf){
        return new ArrayList<Date>(){{
            for (String strDate : strDateList) {
                try {
                    add(sdf.parse(strDate));
                } catch (ParseException e) {
                    System.err.println("部分日期转换失败！");
                    throw new RuntimeException(e);
                }
            }
        }};

    }


    /**
     * 日期字符集合转换为Date日期集合   默认日期格式为 yyyy-MM-dd
     * @param strDateList 日期字符串
     * @return {@link List}<{@link Date}>
     */
    public static List<Date> strListToDateList(List<String> strDateList){
        SimpleDateFormat sdf=new SimpleDateFormat(PATTERN1);
        return strListToDateList(strDateList,sdf);

    }

    /**
     *  获取间隔日期 天数  格式为 yyyy-mm-dd
     * @param dateStrStart 开始日期
     * @param dateStrEnd 结束日期
     * @return {@link Integer}
     */
    public Long getIntervalDateDays(String dateStrStart,String dateStrEnd){
        // 将日期字符串转换为 LocalDate 对象
        LocalDate localDate1 = LocalDate.parse(dateStrStart);
        LocalDate localDate2 = LocalDate.parse(dateStrEnd);
        // 计算两个日期之间的天数差
        return ChronoUnit.DAYS.between(localDate1, localDate2);
    }
}
